CM3D2 Converter.misc_VIEW3D_MT_edit_mesh_merge
1import numpy as np 2import bpy 3import bmesh 4from . import common 5from . import compat 6 7# メニュー等に項目追加 8def menu_func(self, context): 9 icon_id = common.kiss_icon() 10 self.layout.separator() 11 self.layout.label(text="CM3D2", icon_value=icon_id) 12 self.layout.operator('mesh.remove_and_mark_doubles') 13 14@compat.BlRegister() 15class CNV_OT_remove_and_mark_doubles(bpy.types.Operator): 16 bl_idname = 'mesh.remove_and_mark_doubles' 17 bl_label = "Remove and Mark Doubles" 18 bl_description = "Remove doubles while marking merged geometry as seams and/or sharp edges" 19 bl_options = {'REGISTER', 'UNDO'} 20 21 threshold = bpy.props.FloatProperty(name="Merge Distance" , default=0.0001, description="Maximum distance between elements to merge") 22 normal_threshold = bpy.props.FloatProperty(name="Normal Angle" , default=0.0000, description="Maximum angle between element's normals to mark sharp") 23 use_unselected = bpy.props.BoolProperty(name="Unselected" , default=False , description="Merge selected to other unselected vertices") 24 keep_custom_normals = bpy.props.BoolProperty(name="Keep Custom Normals", default=True , description="Keep custom split normals") 25 mark_sharp = bpy.props.BoolProperty(name="Mark Sharp" , default=True , description="Mark sharp") 26 27 @classmethod 28 def poll(cls, context): 29 ob = context.active_object 30 return ob and ob.type == 'MESH' and ob.mode == 'EDIT' 31 32 def execute(self, context): 33 ob = context.active_object 34 me = ob.data 35 bm = bmesh.from_edit_mesh(me) 36 37 selected_verts = bm.verts if len(bm.select_history) <= 0 else set( filter(lambda v : v.select, bm.verts) ) 38 search_verts = bm.verts if self.use_unselected else selected_verts 39 40 targetmap = bmesh.ops.find_doubles(bm, verts=search_verts, dist=self.threshold, 41 keep_verts=selected_verts if self.use_unselected else list())['targetmap'] 42 43 selected_edges = bm.edges if len(bm.select_history) <= 0 else set( filter(lambda e : e.verts[0].select or e.verts[1].select, bm.edges) ) 44 45 # メッシュ整頓 46 if me.has_custom_normals: 47 layer = bm.loops.layers.float_vector.new('custom_normals.temp') 48 set_bmlayer_from_custom_normals(bm, layer, me) 49 50 if self.is_sharp: 51 pass 52 53 # Remove Doubles 54 bmesh.ops.weld_verts(bm, targetmap=targetmap) 55 bmesh.update_edit_mesh(me) 56 57 if me.has_custom_normals: 58 if self.keep_custom_normals: 59 set_custom_normals_from_bmlayer(bm, layer, me) 60 bm.loops.layers.float_vector.remove(layer) 61 bmesh.update_edit_mesh(me) 62 63 return {'FINISHED'} 64 65 @staticmethod 66 def set_bmlayer_from_custom_normals(bm, layer, me): 67 me.calc_normals_split() 68 for face in bm.faces: 69 for loop in face.loops: 70 loop[layer] = me.loops[loop.index].normal 71 72 @staticmethod 73 def set_custom_normals_from_bmlayer(bm, layer, me): 74 custom_normals = np.zeros((len(me.loops), 3)) 75 for face in bm.faces: 76 for loop in face.loops: 77 custom_normals[loop.index] = loop[layer] 78 me.normals_split_custom_set(custom_normals)
@compat.BlRegister()
class
CNV_OT_remove_and_mark_doubles15@compat.BlRegister() 16class CNV_OT_remove_and_mark_doubles(bpy.types.Operator): 17 bl_idname = 'mesh.remove_and_mark_doubles' 18 bl_label = "Remove and Mark Doubles" 19 bl_description = "Remove doubles while marking merged geometry as seams and/or sharp edges" 20 bl_options = {'REGISTER', 'UNDO'} 21 22 threshold = bpy.props.FloatProperty(name="Merge Distance" , default=0.0001, description="Maximum distance between elements to merge") 23 normal_threshold = bpy.props.FloatProperty(name="Normal Angle" , default=0.0000, description="Maximum angle between element's normals to mark sharp") 24 use_unselected = bpy.props.BoolProperty(name="Unselected" , default=False , description="Merge selected to other unselected vertices") 25 keep_custom_normals = bpy.props.BoolProperty(name="Keep Custom Normals", default=True , description="Keep custom split normals") 26 mark_sharp = bpy.props.BoolProperty(name="Mark Sharp" , default=True , description="Mark sharp") 27 28 @classmethod 29 def poll(cls, context): 30 ob = context.active_object 31 return ob and ob.type == 'MESH' and ob.mode == 'EDIT' 32 33 def execute(self, context): 34 ob = context.active_object 35 me = ob.data 36 bm = bmesh.from_edit_mesh(me) 37 38 selected_verts = bm.verts if len(bm.select_history) <= 0 else set( filter(lambda v : v.select, bm.verts) ) 39 search_verts = bm.verts if self.use_unselected else selected_verts 40 41 targetmap = bmesh.ops.find_doubles(bm, verts=search_verts, dist=self.threshold, 42 keep_verts=selected_verts if self.use_unselected else list())['targetmap'] 43 44 selected_edges = bm.edges if len(bm.select_history) <= 0 else set( filter(lambda e : e.verts[0].select or e.verts[1].select, bm.edges) ) 45 46 # メッシュ整頓 47 if me.has_custom_normals: 48 layer = bm.loops.layers.float_vector.new('custom_normals.temp') 49 set_bmlayer_from_custom_normals(bm, layer, me) 50 51 if self.is_sharp: 52 pass 53 54 # Remove Doubles 55 bmesh.ops.weld_verts(bm, targetmap=targetmap) 56 bmesh.update_edit_mesh(me) 57 58 if me.has_custom_normals: 59 if self.keep_custom_normals: 60 set_custom_normals_from_bmlayer(bm, layer, me) 61 bm.loops.layers.float_vector.remove(layer) 62 bmesh.update_edit_mesh(me) 63 64 return {'FINISHED'} 65 66 @staticmethod 67 def set_bmlayer_from_custom_normals(bm, layer, me): 68 me.calc_normals_split() 69 for face in bm.faces: 70 for loop in face.loops: 71 loop[layer] = me.loops[loop.index].normal 72 73 @staticmethod 74 def set_custom_normals_from_bmlayer(bm, layer, me): 75 custom_normals = np.zeros((len(me.loops), 3)) 76 for face in bm.faces: 77 for loop in face.loops: 78 custom_normals[loop.index] = loop[layer] 79 me.normals_split_custom_set(custom_normals)
threshold: <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Merge Distance', 'default': 0.0001, 'description': 'Maximum distance between elements to merge', 'attr': 'threshold'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Merge Distance', 'default': 0.0001, 'description': 'Maximum distance between elements to merge', 'attr': 'threshold'}>
normal_threshold: <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Normal Angle', 'default': 0.0, 'description': "Maximum angle between element's normals to mark sharp", 'attr': 'normal_threshold'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Normal Angle', 'default': 0.0, 'description': "Maximum angle between element's normals to mark sharp", 'attr': 'normal_threshold'}>
use_unselected: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Unselected', 'default': False, 'description': 'Merge selected to other unselected vertices', 'attr': 'use_unselected'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Unselected', 'default': False, 'description': 'Merge selected to other unselected vertices', 'attr': 'use_unselected'}>
keep_custom_normals: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Keep Custom Normals', 'default': True, 'description': 'Keep custom split normals', 'attr': 'keep_custom_normals'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Keep Custom Normals', 'default': True, 'description': 'Keep custom split normals', 'attr': 'keep_custom_normals'}>
mark_sharp: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Mark Sharp', 'default': True, 'description': 'Mark sharp', 'attr': 'mark_sharp'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Mark Sharp', 'default': True, 'description': 'Mark sharp', 'attr': 'mark_sharp'}>
def
execute(self, context):
33 def execute(self, context): 34 ob = context.active_object 35 me = ob.data 36 bm = bmesh.from_edit_mesh(me) 37 38 selected_verts = bm.verts if len(bm.select_history) <= 0 else set( filter(lambda v : v.select, bm.verts) ) 39 search_verts = bm.verts if self.use_unselected else selected_verts 40 41 targetmap = bmesh.ops.find_doubles(bm, verts=search_verts, dist=self.threshold, 42 keep_verts=selected_verts if self.use_unselected else list())['targetmap'] 43 44 selected_edges = bm.edges if len(bm.select_history) <= 0 else set( filter(lambda e : e.verts[0].select or e.verts[1].select, bm.edges) ) 45 46 # メッシュ整頓 47 if me.has_custom_normals: 48 layer = bm.loops.layers.float_vector.new('custom_normals.temp') 49 set_bmlayer_from_custom_normals(bm, layer, me) 50 51 if self.is_sharp: 52 pass 53 54 # Remove Doubles 55 bmesh.ops.weld_verts(bm, targetmap=targetmap) 56 bmesh.update_edit_mesh(me) 57 58 if me.has_custom_normals: 59 if self.keep_custom_normals: 60 set_custom_normals_from_bmlayer(bm, layer, me) 61 bm.loops.layers.float_vector.remove(layer) 62 bmesh.update_edit_mesh(me) 63 64 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- items
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data